/*
 * Copyright 2011-2013 Blender Foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "stdosl.h"
#include "node_texture.h"

void voronoi_m(point p, string metric, float e, float da[4], point pa[4])
{
  /* Compute the distance to and the position of the four closest neighbors to p.
   *
   * The neighbors are randomly placed, 1 each in a 3x3x3 grid (Worley pattern).
   * The distances and points are returned in ascending order, i.e. da[0] and pa[0] will
   * contain the distance to the closest point and its coordinates respectively.
   */
  int xx, yy, zz, xi, yi, zi;

  xi = (int)floor(p[0]);
  yi = (int)floor(p[1]);
  zi = (int)floor(p[2]);

  da[0] = 1e10;
  da[1] = 1e10;
  da[2] = 1e10;
  da[3] = 1e10;

  for (xx = xi - 1; xx <= xi + 1; xx++) {
    for (yy = yi - 1; yy <= yi + 1; yy++) {
      for (zz = zi - 1; zz <= zi + 1; zz++) {
        point ip = point(xx, yy, zz);
        point vp = (point)cellnoise_color(ip);
        point pd = p - (vp + ip);

        float d = 0.0;
        if (metric == "distance") {
          d = dot(pd, pd);
        }
        else if (metric == "manhattan") {
          d = fabs(pd[0]) + fabs(pd[1]) + fabs(pd[2]);
        }
        else if (metric == "chebychev") {
          d = max(fabs(pd[0]), max(fabs(pd[1]), fabs(pd[2])));
        }
        else if (metric == "minkowski") {
          d = pow(pow(fabs(pd[0]), e) + pow(fabs(pd[1]), e) + pow(fabs(pd[2]), e), 1.0 / e);
        }

        vp += point(xx, yy, zz);

        if (d < da[0]) {
          da[3] = da[2];
          da[2] = da[1];
          da[1] = da[0];
          da[0] = d;

          pa[3] = pa[2];
          pa[2] = pa[1];
          pa[1] = pa[0];
          pa[0] = vp;
        }
        else if (d < da[1]) {
          da[3] = da[2];
          da[2] = da[1];
          da[1] = d;

          pa[3] = pa[2];
          pa[2] = pa[1];
          pa[1] = vp;
        }
        else if (d < da[2]) {
          da[3] = da[2];
          da[2] = d;

          pa[3] = pa[2];
          pa[2] = vp;
        }
        else if (d < da[3]) {
          da[3] = d;
          pa[3] = vp;
        }
      }
    }
  }
}

/* Voronoi */

shader node_voronoi_texture(
    int use_mapping = 0,
    matrix mapping = matrix(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
    string coloring = "intensity",
    string metric = "distance",
    string feature = "F1",
    float Exponent = 1.0,
    float Scale = 5.0,
    point Vector = P,
    output float Fac = 0.0,
    output color Color = 0.0)
{
  point p = Vector;

  if (use_mapping)
    p = transform(mapping, p);

  /* compute distance and point coordinate of 4 nearest neighbours */
  float da[4];
  point pa[4];

  /* compute distance and point coordinate of 4 nearest neighbours */
  voronoi_m(p * Scale, metric, Exponent, da, pa);

  if (coloring == "intensity") {
    /* Intensity output */
    if (feature == "F1") {
      Fac = fabs(da[0]);
    }
    else if (feature == "F2") {
      Fac = fabs(da[1]);
    }
    else if (feature == "F3") {
      Fac = fabs(da[2]);
    }
    else if (feature == "F4") {
      Fac = fabs(da[3]);
    }
    else if (feature == "F2F1") {
      Fac = fabs(da[1] - da[0]);
    }
    Color = color(Fac);
  }
  else {
    /* Color output */
    if (feature == "F1") {
      Color = pa[0];
    }
    else if (feature == "F2") {
      Color = pa[1];
    }
    else if (feature == "F3") {
      Color = pa[2];
    }
    else if (feature == "F4") {
      Color = pa[3];
    }
    else if (feature == "F2F1") {
      Color = fabs(pa[1] - pa[0]);
    }

    Color = cellnoise_color(Color);
    Fac = (Color[0] + Color[1] + Color[2]) * (1.0 / 3.0);
  }
}
